/** 
  * 控制台使用中文报错 yarn tsc --locale zh-cn 
  * 不同文件中相同的变量名称会报错，文件末尾使用export {}
*/

const hello = (name: string) => {
  console.log(`hello: ${name}`)
}

hello('tom')

// 原始数据类型
const str: string = 'str'
const num: number = 100 || NaN || Infinity
const bool: boolean = true || false
const v: void = undefined
const n: null = null
const undef: undefined = undefined
const s: symbol = Symbol() // es6属性报错的话设置配置文件中的lib

// object类型
const foo: object = function () {} // [] || {}
const obj1: {} = { foo: 12 }
const obj2: { foo: number, bar: string } = { foo: 12, bar: '1' }

// 数组类型
const arr1: Array<number | string> = [1, 2, 3, '6', '7', '8']
const arr2: number[] = [1, 2, 3]
function sum (...args: number[]) {
  return args.reduce((total, current) => total + current, 0)
}
sum(1, 2, 3)

// 元组类型
const tuple: [number, string] = [18, 'tom']
const tupleAge = tuple[0]
const tupleName = tuple[1]
const [age, name] = tuple
// Object.entries({ 返回元组
//   foo: 123,
//   bar: 456,
// })


// 枚举类型
// 数字枚举，不指定值，会自动从0累加，指定某个值后，之后的数据会从指定值的基础上继续累加
enum PostStatus {
  Draft,
  Unpublished,
  Published,
}

// 常量枚举，不能通过下标获取
const enum ConstPostStatus {
  Draft,
  Unpublished,
  Published,
}

// 字符传枚举，需要手动为指定值后的每个成员初始化枚举值
enum StringPostStatus {
  Draft = 'a', // 没指定时为0
  Unpublished = 'b',
  Published = 'c',
}

const post = {
  title: 'Hello TS',
  content: 'this is typescipit',
  stauts: PostStatus.Draft // 非常量枚举可以通过下标PostStatus[0]获取
}

const postConst = {
  title: 'Hello TS',
  content: 'this is typescipit',
  stauts: ConstPostStatus.Draft // 常量枚举不可以通过下标ConstPostStatus[0]获取
}

const postString = {
  title: 'Hello TS',
  content: 'this is typescipit',
  stauts: PostStatus.Draft
}

// 函数类型 b?: number
function func1 (a: number, b: number = 100, ...rest: number[]): string {
  return JSON.stringify(a + b)
}
func1(100, 100, 300, 600, 1000)
const func2: (a: number, b: number) => string = function (a: number, b: number): string {
  return JSON.stringify(a + b)
}

// 任意类型（弱类型）
function stringify (val: any) {
  return JSON.stringify(val)
}
stringify('val')
stringify(100)
let fooAny: any = 'str'
fooAny = 100

// 隐式类型推断
let foo1 // 声明式未定义，推断为any
foo1 = 18
let age1 = 18 // 未声明类型，推断为number
let str1 = '100' // 未声明类型，推断为string

// 类型断言
const nums = [110, 120, 119, 112]
const res = nums.find(i => i > 0)
const num1 = res as number // 断言res一定为number
const num2 = <number>res // 断言res一定为number，此方法在jsx下不能使用，会有冲突
const square = num1 * num1

// 接口，约束对象成员
interface Post {
  title: string
  content: string
  publishNum: number
  subTitle?: string // 可选成员
  readonly summary?: string // 只读成员
  // [prop: string]: string // 指定动态成员 post.axx = xxx
}
function printPost (post: Post) {
  console.log(post.title)
  console.log(post.content)
  console.log(post.publishNum)
}
printPost({
  title: 'typescript',
  content: 'test',
  publishNum: 1
})

// 类
// 通过接口为类定义公共方法，尽可能的细化接口，方便更多的类继承
interface Eat {
  eat (food: string): void
}
interface Run {
  run (distance: number): void
}

//抽象类，只能被继承，不能被new创建实例对象
abstract class Animal implements Eat {
  eat (food: string): void {
    console.log(food)
  }

  abstract run (distance: number): void // 抽象方法，子类必须去实现此方法
}
class Dog extends Animal {
  run(distance: number): void {
    console.log('跑跑')
  }
}

class Person implements Eat, Run {
  public name: string // = 'init' // 公有属性，默认都为公有属性
  private age: number // 私有属性只能在类的内部访问
  protected readonly gender: boolean // 受保护属性，只能在类和子类中去访问，只读属性不允许修改

  constructor (name: string, age: number) {
    this.name = name // 构造函数里使用this访问属性，需要先在类中声明此属性
    this.age = age
    this.gender = true
  }

  sayHi (msg: string): void {
    console.log(`I am ${this.name}, ${msg}`)
  }

  eat (food: string): void {
    console.log(food)
  }

  run (distance: number): void {
    console.log(distance)
  }
}

class Student extends Person {
  // 构造函数添加私有属性后，在外部不能被实例化，只能通过内部静态方法访问
  private constructor (name: string, age: number) {
    super(name, age)
    console.log(this.gender) // 访问父类中的受保护属性
  }

  static create (name: string, age: number) {
    return new Student(name, age)
  }
}

const stu = Student.create('jack', 18)
const tom = new Person('tom', 18)

// 泛型
function createNumArr (length: number, value: number): number[] {
  const arr = Array<number>(length).fill(value)
  return arr
}

// 使用时确定类型
function createArr<T> (length: number, value: T): T[] {
  const arr = Array<T>(length).fill(value)
  return arr
}
type StringOrNumber = string | number
const res1 = createArr<StringOrNumber>(3, 'foo')

// 对第三方模块类型声明
// declare function camelCase (params: string): string

export {}
